from .listener import *

class ModelListPanelFuncs(ModelListPanelListener):

    def __init__(self, parent=None):
        super().__init__(parent=parent)

    def _root_right_click(self):
        '''树  根节点  右击'''
        menu = wx.Menu()
        menu.Append(self.popup_root_new_app, "创建应用程序")
        menu.AppendSeparator()
        menu.Append(self.popup_root_export_database, "导出数据库/备份")
        menu.AppendSeparator()
        menu.Append(self.popup_root_migrate_database, "数据源一键切换[结构+数据]")
        menu.AppendSeparator()
        menu.Append(self.popup_root_recycle_bin, "回收站")
        # menu.AppendSeparator()
        # menuVSCode = wx.Menu()
        # menuVSCode.Append(self.popup_open_view_path, "打开views文件")
        # menuVSCode.Append(self.popup_open_url_path, "打开urls文件")
        # menuVSCode.Append(self.popup_open_model_path, "打开models文件")
        # menuVSCode.Append(self.popup_open_template_path, "打开template文件")
        # menu.Append(wx.ID_ANY, "&Visual Studio Code", menuVSCode)
        self.PopupMenu(menu)
        menu.Destroy()

    def _second_root_right_click(self):
        """第二根节点右击"""
        menu = wx.Menu()
        menu.Append(self.popup_root_new_model, "创建模型")
        menu.AppendSeparator()
        menu.Append(self.popup_root_import_model, "导入模型")
        self.PopupMenu(menu)
        menu.Destroy()

    def _third_node_right_click(self, item_name):
        """第三节点右击"""
        menu = wx.Menu()
        menu.Append(self.popup_node_delete_model, f"删除{item_name}")
        menu.AppendSeparator()
        menu.Append(self.popup_node_export_model, f"导出{item_name}")
        menu.AppendSeparator()
        menu.Append(self.popup_node_register_model, f"注册{item_name}到后台")
        menu.AppendSeparator()
        menu.Append(self.popup_node_model_Meta, f"Meta选项")
        menu.AppendSeparator()

        menuRestFrameWork = wx.Menu()
        # menuRestFrameWork.Append(self.popup_node_rf_Serializer, "serializers.Serializer")
        # menuRestFrameWork.AppendSeparator()
        menuRestFrameWork.Append(self.popup_node_rf_ModelSerializer, "serializers.ModelSerializer")
        menu.Append(wx.ID_ANY, "&Rest Framework", menuRestFrameWork)
        menu.AppendSeparator()

        menuVSCode = wx.Menu()
        menuVSCode.Append(self.popup_node_vscode_open_model, "在models文件中打开")
        menu.Append(wx.ID_ANY, "&Visual Studio Code", menuVSCode)
        self.PopupMenu(menu)
        menu.Destroy()

    def onRightTreeClick(self, evt):
        '''右击节点事件'''
        # 右击时，选中右击的节点
        right_click_item = evt.GetItem()
        item_name = self.treeCtrl.GetItemText(right_click_item)
        self.right_click_tree_node_name = item_name
        '''
            记录右键点击的节点及其父节点
        '''
        p_node = self.treeCtrl.GetItemParent(right_click_item)
        if p_node.IsOk():
            self.open_model_name = item_name
            self.open_app_name = self.treeCtrl.GetItemText(p_node)

        '''
            模拟选中，焦点赋予当前选中的节点
        '''
        self.treeCtrl.SelectItem(right_click_item)

        '''
            判断当前选中节点类型，根据不同的类型，弹出
        '''
        if self.treeCtrl.GetRootItem() == right_click_item:
            self._root_right_click()
        else:
            if self.treeCtrl.GetRootItem() == self.treeCtrl.GetItemParent(right_click_item):
                self._second_root_right_click()
            else:
                self._third_node_right_click(item_name)

    def onBtnRefresh(self, evt):
        '''刷新树结构数据'''
        self.treeCtrl.DeleteAllItems()
        self._init_tree_data()

    def onActivate(self, evt):
        '''树节点双击事件'''
        choice_item = evt.GetItem()
        if self.treeCtrl.GetRootItem() != choice_item and self.treeCtrl.GetRootItem() != self.treeCtrl.GetItemParent(choice_item):
            # RichMsgDialog.showOkMsgDialog(self, self.treeCtrl.GetItemText(choice_item), "提示")
            self.open_model_name = model_name = self.treeCtrl.GetItemText(choice_item)
            self.open_app_name = app_name = self.treeCtrl.GetItemText(self.treeCtrl.GetItemParent(choice_item))
            '''
                用 self.model_fields 刷新树形列表控件
                1、先构建数据包
                2、重新刷新控件
            '''
            if not self.data_context[CONTEXT_REF_TREELIST]:
                self.model_fields = djangotools.get_fields_by_modelname(app_name, model_name)
                self._init_listree_data(self.model_fields, True, model_name, self.checkBoxHave.IsChecked(), self.data_context) # 刷新数据，True表示手动，非页面自动加载
                self.rightPanel.Layout() # 及时更新
            else:
                RichMsgDialog.showOkMsgDialog(self, '操作冲突，请等待之前的更新操作完成', '警告')
            
        else:
            if self.treeCtrl.IsExpanded(choice_item):
                self.treeCtrl.Collapse(choice_item)
            else:
                self.treeCtrl.Expand(choice_item)

    def onItemExpanded(self, event):
        '''节点展开事件'''
        # item = event.GetItem()

    def onItemCollapsed(self, event):
        '''节点收起事件'''
        # item = event.GetItem()

    def onSelChanged(self, event):
        '''选项点击事件'''
        self.select_node = event.GetItem()
        event.Skip()
        # print('OnSelChanged', self.select_node)

    def onPopupNewApp(self, evt):
        """新增应用程序"""
        if self.select_node is not None and self.select_node.IsOk():
            # app_name = self.treeCtrl.GetItemText(self.select_node)
            app_name = RichMsgDialog.showAskQuestionDialog(self, '请为您的应用程序命名：', '新增应用程序')
            status = djangotools.create_new_app_easy(app_name)
            if -1 == status:
                '''同名/命名不规范'''
                RichMsgDialog.showOkMsgDialog(self, '应用程序已存在，或命名不规范（字母+下划线）！', '错误')
            elif 0 == status:
                '''创建失败'''
                RichMsgDialog.showOkMsgDialog(self, '无法创建应用程序！', '错误')
            elif 2 == status:
                '''创建成功，注册失败'''
                RichMsgDialog.showOkMsgDialog(self, '应用程序创建成功，settings.py注册失败！', '错误')
            elif 3 == status:
                '''创建成功，注册成功，路由未修复'''
                RichMsgDialog.showOkMsgDialog(self, '应用程序创建成功，settings.py注册成功，路由修复失败！', '错误')
            else:
                '''创建成功'''
                RichMsgDialog.showOkMsgDialog(self, '应用程序创建成功！', '成功')
            
            # 刷新树形控件
            self.treeCtrl.DeleteAllItems()
            self._init_tree_data()

    def onPopupNewModel(self, evt):
        """新增模型"""
        if self.right_click_tree_node_name is None:
            return

        model_name = RichMsgDialog.showAskQuestionDialog(self, f"正在为【{self.right_click_tree_node_name}】应用程序创建模型，请为您的模型指定类名：", "新增模型")
        if model_name is None:
            RichMsgDialog.showOkMsgDialog(self, '变量名不允许为空', '错误')
            return

        paths: List[str] = []
        for _ in self.tree_apps_models_struct:
            appname, modelnames, path = _
            if self.right_click_tree_node_name == appname:
                paths.append(path) # 获取当前应用程序下的所有合法路径，作为插入的依据
        select_path = RichMsgDialog.showSingleChoiceDialog(self, f'选择模型【{model_name}】写入的文件路径', '选择路径', paths)
        if select_path is None:
            return

        djangotools.create_empty_model(model_name, select_path)
        # 刷新树形控件
        self.treeCtrl.DeleteAllItems()
        self._init_tree_data()

        RichMsgDialog.showOkMsgDialog(self, f'模型【{model_name}】创建成功，写入路径：{select_path}', '成功')

    def onPopupDeleteModel(self, evt):
        """删除模型"""
        if RichMsgDialog.showYesNoMsgDialog(self, '确认删除此模型？', "警告"):
            '''执行删除操作'''

    def _get_path_from_struct(self, app_name: str, model_name: str) -> str:
        '''通过 app_name 和 model_name 获取路径
        
            无匹配项返回 None【理论上不会存在返回None的情况】
        '''
        if not app_name or not model_name:
            return None
        for _ in self.tree_apps_models_struct:
            appname, modelnames, path = _
            if app_name == appname and model_name in modelnames:
                return path
        return None

    def onPopupVSCodeOpenModel(self, evt):
        '''打开模型所在文件中的位置'''
        path = self._get_path_from_struct(self.open_app_name, self.open_model_name)
        row, col, model_path = djangotools.get_location_from_modelspy(self.open_app_name, self.open_model_name) # 光标定位
        if model_path:
            cmd = f"code -g {model_path}:{row}:{col}" # 原生命令打开VSCode
            wx.Shell(cmd)
        else:
            RichMsgDialog.showOkMsgDialog(self, "无法打开", "错误")

    def onPopupNodeModelMeta(self, evt):
        """Meta选项"""
        dlg = dialogFields.ModelMetaDialog(self
            , self.open_app_name
            , self.open_model_name
            , self._get_path_from_struct(self.open_app_name, self.open_model_name)
        )
        dlg.ShowModal()
        dlg.Close(True)

    def onPopupNodeRFModelSerializer(self, evt):
        '''Rest Framework 模型序列化'''
        choices = djangotools.get_fields_by_modelname(self.open_app_name, self.open_model_name)
        dlg = dialogRestFramework.ModelSerializerDialog(self
            , self.open_app_name
            , self.open_model_name
            , list(choices.keys())
        )
        dlg.ShowModal()
        dlg.Close(True)
        

    ##################################################
    ''' ==============================================
        ============ 树形-列表鼠标点击事件 ==============
    '''#==============================================
    ##################################################

    def onItemDBClick(self, evt):
        """行双击事件"""
        item = evt.GetItem()
        if self.treeListData.IsExpanded(item):
            self.treeListData.Collapse(item)
        else:
            self.treeListData.Expand(item)

    def onItemRightClick(self, evt):
        """右击弹出菜单"""
        item = evt.GetItem()
        menu = wx.Menu()
        if self.model.IsRowData(item):
            menu.Append(self.popup_treelist_modify_field, "修改字段")
            menu.AppendSeparator()
            menu.Append(self.popup_treelist_delete_field, "删除字段")
            menu.AppendSeparator()
            menu.Append(self.popup_treelist_copy_field, "拷贝字段")
        else:
            self.treelist_node_name = '-'.join(self.model.GetValue(evt.GetItem(), 0).split('-')[1:])
            menu.Append(self.popup_treelist_add_field, f"新增【{self.treelist_node_name}】字段")
            # menu.AppendSeparator()

            # menuVSCode = wx.Menu()
            # menuVSCode.Append(self.popup_open_view_path, "打开views文件")
            # menuVSCode.Append(self.popup_open_url_path, "打开urls文件")
            # menuVSCode.Append(self.popup_open_model_path, "打开models文件")
            # menuVSCode.Append(self.popup_open_template_path, "打开template文件")
            # menu.Append(wx.ID_ANY, "&Visual Studio Code", menuVSCode)

            # menu.AppendSeparator()
            # menu.Append(self.popup_modify_url, "修改路由路径")
            # menu.AppendSeparator()
            # menu.Append(self.popup_get_code, "URL的反向解析")
            # menu.Append(self.popup_insert_func, "插入功能")
            # menu.AppendSeparator()
            # menu.Append(self.popup_show_mvc_mtv, "MVC/MTV数据流概览")
        self.PopupMenu(menu)
        menu.Destroy()

    def onItemSelect(self, e):
        """行选择"""
        obj: wxdv.DataViewItem = e.GetItem() # 主要用于检测行是否正确选中数据行
        if obj.IsOk():
            self.select_row = self.model.GetRowDictValue(obj)
        else:
            self.select_row = {}

    def onPopupTreelistAddField(self, evt):
        '''树形列表--新增字段'''
        dlg = dialogFields.FieldCreateDialog(self
            , self.treelist_node_name # 当前选中的 树形列表 的节点名
            , self.open_app_name # 当前选中应用程序
            , self.open_model_name # 当前选中模型名
            , self._get_path_from_struct(self.open_app_name, self.open_model_name) # 模型路径
            , self.tree_apps_models_struct # 所有的应用程序名及其所属的所有模型名
            , self.model_fields # 当前选中模型下的所有字段集合
        )
        dlg.ShowModal()
        # dlg.Close(True)

        '''
            数据改变，刷新界面以及数据包【会在对话框销毁后，线性执行刷新代码】
        '''
        self.model_fields = djangotools.get_fields_by_modelname(self.open_app_name, self.open_model_name)
        self._init_listree_data(self.model_fields, True, self.open_model_name, self.checkBoxHave.IsChecked(), self.data_context)
        self.rightPanel.Layout()
        

    ##################################################
    ''' ==============================================
        ==================== 其它 ====================
    '''#==============================================
    ##################################################

    def onCheckBoxHave(self, e: wx.CommandEvent):
        '''这里监听复选框的值变化
        
            此处存在BUG：当快速取消/选中时，软件会崩溃（暂时解决不了，先取消该功能）
        '''
        if not env.IsMac:
            if self.open_app_name is not None and self.open_model_name is not None:
                if not self.data_context[CONTEXT_REF_TREELIST]:
                    self.model_fields = djangotools.get_fields_by_modelname(self.open_app_name, self.open_model_name)
                    self._init_listree_data(self.model_fields, True, self.open_model_name, self.checkBoxHave.IsChecked(), self.data_context)
                    self.rightPanel.Layout()
                else:
                    self.checkBoxHave.SetValue(not e.IsChecked())
                    e.Veto()
                    RichMsgDialog.showOkMsgDialog(self, '操作冲突，请等待之前的更新操作完成', '警告')
            else:
                self.checkBoxHave.SetValue(not e.IsChecked())
                e.Veto()

    def onBtnExpandAll(self, evt: wx.CommandEvent):
        '''展开/收起所有树形列表节点
        
            目前在 Mac 上此方法不生效，可能 Mac 不支持 wxpython 有关的 api
        '''
        label = self.btn_expand_all.GetLabel()
        self.treeListData.SelectAll() # Mac 上 此方法无效
        if BUTTON_EXPAND_ALL == label:
            for item in self.treeListData.GetSelections():
                if not self.treeListData.IsExpanded(item):
                    self.treeListData.Expand(item)
            self.btn_expand_all.SetLabel(BUTTON_COLLAPSE_ALL)
        else:
            for item in self.treeListData.GetSelections():
                if self.treeListData.IsExpanded(item):
                    self.treeListData.Collapse(item)
            self.btn_expand_all.SetLabel(BUTTON_EXPAND_ALL)
        self.treeListData.UnselectAll()
        self.rightPanel.Layout() # Mac上更新使用

    def onSpliterDBClick(self, evt):
        evt.Veto()
        